use std::collections::HashMap;

use serde::{Deserialize, Serialize};
use utoipa::ToSchema;

use crate::models::entity::perm;

/// 菜单详情信息
#[derive(Debug, Deserialize, Serialize, ToSchema)]
pub struct MenuDetail {
    pub id: i32,
    pub name: String,
    pub desc: Option<String>,
    pub parent_id: i32,
    pub order: i32,
    pub path: String,
    pub create_time: String,
    pub update_time: String,
}

/// 菜单列表信息
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct MenuListInfo {
    pub id: i32,
    pub name: String,
    pub desc: Option<String>,
    pub parent_id: i32,
    pub order: i32,
    pub path: String,
    pub children: Vec<MenuListInfo>,
}

impl From<perm::Model> for MenuDetail {
    fn from(value: perm::Model) -> Self {
        let fmt = "%Y-%m-%d %H:%M:%S";
        Self {
            id: value.id,
            name: value.name,
            desc: value.desc,
            parent_id: value.parent_id,
            path: value.path,
            create_time: value.create_at.format(fmt).to_string(),
            update_time: value.update_at.format(fmt).to_string(),
            order: value.order,
        }
    }
}

impl From<perm::Model> for MenuListInfo {
    fn from(value: perm::Model) -> Self {
        Self {
            id: value.id,
            name: value.name,
            desc: value.desc,
            parent_id: value.parent_id,
            order: value.order,
            path: value.path,
            children: vec![],
        }
    }
}
impl MenuListInfo {
    pub fn build_tree(menu_list: Vec<perm::Model>) -> Vec<MenuListInfo> {
        let mut menu_map: HashMap<i32, Vec<MenuListInfo>> = HashMap::new();
        let mut root_menus: Vec<MenuListInfo> = Vec::new();

        for menu in menu_list {
            let mut menu = MenuListInfo::from(menu);
            menu.children = Vec::new();
            if menu.parent_id == 0 {
                root_menus.push(menu);
            } else {
                menu_map
                    .entry(menu.parent_id)
                    .or_default()
                    .push(menu);
            }
        }

        fn build_children(menu: &mut MenuListInfo, menu_map: &HashMap<i32, Vec<MenuListInfo>>) {
            if let Some(children) = menu_map.get(&menu.id) {
                let mut sorted_children = children.clone();
                sorted_children.sort_by_key(|m| m.order);
                for mut child in sorted_children {
                    build_children(&mut child, menu_map);
                    menu.children.push(child);
                }
            }
        }

        for root_menu in &mut root_menus {
            build_children(root_menu, &menu_map);
        }

        root_menus.sort_by_key(|m| m.order);
        root_menus
    }
}
